Sizes

Monitor (physical screen)
  └─ Window frame (decorations)
       └─ Window client area (GLFW window size)
            └─ Framebuffer (pixel resolution)
                 └─ Swapchain images
                      └─ Viewport/scissor (render region)

Monitor size (physical display resolution)

monitor_get_size() -> { 2560, 1440 }

monitor_get_size :: proc(monitor_idx: int = 0) -> (extent: spatial.Extent) {
    monitors := glfw.GetMonitors()
    
    assert(monitor_idx < len(monitors))

    video_mode := glfw.GetVideoMode(monitors[monitor_idx])

    return { u32(video_mode.width), u32(video_mode.height) }
}
  • This is the full screen resolution of the monitor.

  • A window when maximized in the OS (not fullscreen) can be like:

    monitor height: 1440
    window height:  1377
    
    • Difference:

    1440 - 1377 = 63 px
    
    • That space is taken by things like:

      • taskbar/dock

      • window decorations when maximized

      • OS reserved areas

Frame size (window decorations)

glfw.GetWindowFrameSize() -> { 8, 31, 8, 8 }
  • Order is:

    • (left, top, right, bottom)

  • These are the OS window borders, not part of the client area.

  • So your full outer window size is:

    • width  = 2560 + 8 + 8   = 2576

    • height = 1377 + 31 + 8  = 1416

  • This is mostly useful for window positioning.

Window size (logical size)

glfw.GetWindowSize() -> { 1280, 720 }
  • This is the size of the client area in screen coordinates (logical pixels).

  • Excludes OS decorations (title bar, borders)

  • Used for UI/layout logic

  • May differ from framebuffer size on HiDPI displays

  • If the framebuffer size matches, you are effectively at 1.0 content scale.

Framebuffer size (pixel size)

glfw.GetFramebufferSize() -> { 1280, 720 }
  • This is the actual render target pixel resolution.

  • In Vulkan this is the important one because it determines:

    • swapchain image size

    • render pass attachments

    • viewport/scissor defaults

  • Fhe framebuffer is the storage the fragment shader writes into.

  • On HiDPI displays:

    • window size != framebuffer size

  • Example (Retina):

    • window:      1280×720

    • framebuffer: 2560×1440

Swapchain extent (Vulkan image size)

window.swapchain.extent -> { 1280, 720 }
  • This is the size of the images returned by the Vulkan swapchain.

  • It should normally equal the framebuffer size because:

    • swapchain extent == framebuffer size

  • unless you are doing something unusual.

  • This value is what your render pass ultimately writes into.

Viewport size (Vulkan dynamic state)

VkViewport viewport;
viewport.width  = swapchainExtent.width;
viewport.height = swapchainExtent.height;
  • The viewport defines how framebuffer pixels map to NDC, not the size of the framebuffer itself.

  • Think of it as:

    • framebuffer size → storage

    • viewport → mapping region inside that storage

  • As the framebuffer is the storage the fragment shader writes into, the viewport is the mapping that determines where fragments land inside that storage.

  • You can render to only part of the swapchain image by shrinking the viewport.